<?php

declare(strict_types=1);
/**
 * This file is part of MoChat.
 * @link     https://mo.chat
 * @document https://mochat.wiki
 * @contact  group@mo.chat
 * @license  https://github.com/mochat-cloud/mochat/blob/master/LICENSE
 */
namespace MoChat\App\WorkRoom\Logic;

use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Utils\Codec\Json;
use MoChat\App\Corp\Contract\CorpContract;
use MoChat\App\WorkContact\Constants\Room\Status as WorkContactRoomStatus;
use MoChat\App\WorkContact\Constants\Room\Type as WorkContactRoomType;
use MoChat\App\WorkContact\Contract\WorkContactContract;
use MoChat\App\WorkContact\Contract\WorkContactEmployeeContract;
use MoChat\App\WorkContact\Contract\WorkContactRoomContract;
use MoChat\App\WorkEmployee\Contract\WorkEmployeeContract;
use MoChat\App\WorkRoom\Contract\WorkRoomContract;
use MoChat\App\WorkRoom\Event\CreateRoomEvent;
use MoChat\App\WorkRoom\Event\CreateRoomMemberEvent;
use MoChat\App\WorkRoom\Event\DeleteRoomMemberEvent;
use MoChat\App\WorkRoom\Event\UpdateRoomEvent;
use MoChat\App\WorkRoom\Event\UpdateRoomMemberEvent;
use MoChat\Plugin\AutoTag\Action\Dashboard\Traits\AutoContactTag;
use MoChat\Plugin\RoomFission\Contract\RoomFissionContactContract;
use MoChat\Plugin\RoomFission\Contract\RoomFissionContract;
use MoChat\Plugin\RoomTrack\Constants\Event;
use MoChat\Plugin\RoomTrack\Constants\EventName;
use MoChat\Plugin\RoomTrack\Contract\RoomTrackContract;
use Psr\Container\ContainerInterface;
use Psr\EventDispatcher\EventDispatcherInterface;

/**
 * 客户群管理-微信客户群数据同步.
 *
 * Class SyncLogic
 */
class SyncLogic
{
    use AutoContactTag;

    /**
     * @Inject
     * @var WorkRoomContract
     */
    protected $workRoomService;

    /**
     * @Inject
     * @var WorkContactRoomContract
     */
    protected $workContactRoomService;

    /**
     * @Inject
     * @var RoomFissionContract
     */
    protected $roomFissionService;

    /**
     * @Inject
     * @var RoomFissionContactContract
     */
    protected $roomFissionContactService;

    /**
     * @Inject
     * @var WorkEmployeeContract
     */
    protected $workEmployeeService;

    /**
     * @Inject
     * @var WorkContactEmployeeContract
     */
    protected $workContactEmployeeService;

    /**
     * @Inject
     * @var WorkContactContract
     */
    protected $workContactService;

    /**
     * @Inject
     * @var CorpContract
     */
    protected $corpService;

    /**
     * @Inject
     * @var ContainerInterface
     */
    protected $container;

    /**
     * @Inject
     * @var StdoutLoggerInterface
     */
    private $logger;

    /**
     * 互动轨迹表.
     * @Inject
     * @var RoomTrackContract
     */
    private $roomTrackService;

    /**
     * @var EventDispatcherInterface
     */
    private $eventDispatcher;

    /**
     * @param int $corpId 企业ID
     */
    public function handle(int $corpId, array $wxRoom)
    {
        $this->eventDispatcher = $this->container->get(EventDispatcherInterface::class);
        $this->logger->debug(sprintf('客户群同步开始，群ID：%s', $wxRoom['chat_id']));

        $room = $this->workRoomService->getWorkRoomByCorpIdWxChatId($corpId, $wxRoom['chat_id']);
        // 新增
        if (empty($room)) {
            $this->createRoom($corpId, $wxRoom);
            return;
        }

        $this->updateRoom($corpId, $wxRoom, $room);
    }

    /**
     * 创建群聊.
     */
    private function createRoom(int $corpId, array $wxRoom)
    {
        $createRoomData = [
            'corp_id' => $corpId,
            'wx_chat_id' => $wxRoom['chat_id'],
            'name' => ! empty($wxRoom['name']) ? $wxRoom['name'] : '群聊',
            'owner_id' => 0,
            'notice' => isset($wxRoom['notice']) ? $wxRoom['notice'] : '',
            'status' => $wxRoom['status'],
            'create_time' => date('Y-m-d H:i:s', $wxRoom['create_time']),
            'room_max' => count($wxRoom['member_list']) > 200 ? 500 : 200,
            'created_at' => date('Y-m-d H:i:s'),
        ];

        $employee = $this->workEmployeeService->getWorkEmployeeByWxUserIdCorpId($wxRoom['owner'], $corpId, ['id']);
        if (! empty($employee)) {
            $createRoomData['owner_id'] = $employee['id'];
        }

        $roomId = $this->workRoomService->createWorkRoom($createRoomData);
        $this->createRoomMember($corpId, $roomId, $wxRoom['member_list']);

        go(function () use ($corpId, $createRoomData) {
            $this->eventDispatcher->dispatch(new CreateRoomEvent([
                'corpId' => $corpId,
                'rooms' => [$createRoomData],
            ]));
        });
    }

    /**
     * 创建群聊成员.
     */
    private function createRoomMember(int $corpId, int $roomId, array $memberList)
    {
        $createContactRoomsData = [];
        foreach ($memberList as $member) {
            [$contactId, $employeeId] = $this->getContactIdEmployeeId($corpId, $member);

            $createContactRoomData = [
                'wx_user_id' => $member['userid'],
                'contact_id' => $contactId,
                'employee_id' => $employeeId,
                'unionid' => isset($member['unionid']) ? $member['unionid'] : '',
                'room_id' => $roomId,
                'join_scene' => $member['join_scene'],
                'type' => $member['type'],
                'status' => WorkContactRoomStatus::NORMAL,
                'join_time' => date('Y-m-d H:i:s', $member['join_time']),
                'out_time' => '',
                'created_at' => date('Y-m-d H:i:s'),
                'name' => isset($member['name']) ? $member['name'] : '',
            ];
            $createContactRoomsData[] = $createContactRoomData;

            $this->workContactRoomService->createWorkContactRoom($createContactRoomData);
        }

        if (! empty($createContactRoomsData)) {
            $this->recordTrack($corpId, $roomId, $createContactRoomsData);
            go(function () use ($corpId, $createContactRoomsData) {
                $this->eventDispatcher->dispatch(new CreateRoomMemberEvent([
                    'corpId' => $corpId,
                    'rooms' => $createContactRoomsData,
                ]));
            });
        }
    }

    /**
     * 更新群聊.
     */
    private function updateRoom(int $corpId, array $wxRoom, array $room)
    {
        $updateRoomData = [
            'status' => $wxRoom['status'],
            'name' => ! empty($wxRoom['name']) ? $wxRoom['name'] : '群聊',
            'owner_id' => 0,
            'notice' => isset($wxRoom['notice']) ? $wxRoom['notice'] : '',
            'room_max' => count($wxRoom['member_list']) > 200 ? 500 : 200,
            'create_time' => date('Y-m-d H:i:s', $wxRoom['create_time']),
            'updated_at' => date('Y-m-d H:i:s'),
        ];

        $employee = $this->workEmployeeService->getWorkEmployeeByWxUserIdCorpId($wxRoom['owner'], $corpId, ['id']);
        if (! empty($employee)) {
            $updateRoomData['owner_id'] = $employee['id'];
        }

        $this->workRoomService->updateWorkRoomById((int) $room['id'], $updateRoomData);
        $this->updateRoomMember($corpId, (int) $room['id'], $wxRoom['member_list']);

        if (! empty($updateRoomData)) {
            go(function () use ($corpId, $updateRoomData) {
                $this->eventDispatcher->dispatch(new UpdateRoomEvent([
                    'corpId' => $corpId,
                    'rooms' => [$updateRoomData],
                ]));
            });
        }
    }

    /**
     * 更新群聊成员.
     */
    private function updateRoomMember(int $corpId, int $roomId, array $memberList)
    {
        // 客户成员群聊列表
        $contactRoomList = $this->workContactRoomService->getWorkContactRoomsByRoomIds([$roomId], ['id', 'wx_user_id', 'room_id', 'status']);
        if (empty($contactRoomList)) {
            $this->createRoomMember($corpId, $roomId, $memberList);
            return;
        }

        $contactRoomList = array_column($contactRoomList, null, 'wxUserId');

        $memberList = array_column($memberList, null, 'userid');

        $contactRoomUserIds = array_column($contactRoomList, 'wxUserId');
        $memberListUserIds = array_column($memberList, 'userid');
        $deleteUserIds = array_diff($contactRoomUserIds, $memberListUserIds);
        $createUserIds = array_diff($memberListUserIds, $contactRoomUserIds);
        $createMemberList = [];
        $updateMemberList = [];
        $deleteMemberIdList = [];

        foreach ($memberList as $member) {
            if (in_array($member['userid'], $createUserIds)) {
                $createMemberList[] = $member;
                continue;
            }

            $updateMemberList[] = $member;
        }

        foreach ($contactRoomList as $contactRoom) {
            if (in_array($contactRoom['wxUserId'], $deleteUserIds)) {
                $deleteMemberIdList[] = $contactRoom['id'];
                continue;
            }
        }

        if (! empty($createMemberList)) {
            $this->createRoomMember($corpId, $roomId, $createMemberList);
        }

        $updateContactRoomsData = [];
        foreach ($updateMemberList as $member) {
            [$contactId, $employeeId] = $this->getContactIdEmployeeId($corpId, $member);

            $id = $contactRoomList[$member['userid']]['id'];
            $updateContactRoomData = [
                'id' => $contactRoomList[$member['userid']]['id'],
                'contact_id' => $contactId,
                'employee_id' => $employeeId,
                'unionid' => isset($member['unionid']) ? $member['unionid'] : '',
                'join_scene' => $member['join_scene'],
                'type' => $member['type'],
                'status' => WorkContactRoomStatus::NORMAL,
                'join_time' => date('Y-m-d H:i:s', $member['join_time']),
                'out_time' => '',
                'updated_at' => date('Y-m-d H:i:s'),
                'name' => isset($member['name']) ? $member['name'] : '',
            ];

            $updateContactRoomsData[] = $updateContactRoomData;
            $this->workContactRoomService->updateWorkContactRoomById($id, $updateContactRoomData);
        }

        if (! empty($updateContactRoomsData)) {
            go(function () use ($corpId, $updateContactRoomsData) {
                $this->eventDispatcher->dispatch(new UpdateRoomMemberEvent([
                    'corpId' => $corpId,
                    'rooms' => $updateContactRoomsData,
                ]));
            });
        }

        $this->deleteRoomMember($corpId, $deleteMemberIdList);
    }

    /**
     * 删除群聊成员.
     */
    private function deleteRoomMember(int $corpId, array $deleteMemberIdList)
    {
        if (empty($deleteMemberIdList)) {
            return;
        }

        $this->workContactRoomService->updateWorkContactRoomByIds($deleteMemberIdList, [
            'status' => WorkContactRoomStatus::QUIT,
            'out_time' => date('Y-m-d H:i:s'),
        ]);

        go(function () use ($corpId, $deleteMemberIdList) {
            $this->eventDispatcher->dispatch(new DeleteRoomMemberEvent([
                'corpId' => $corpId,
                'rooms' => $deleteMemberIdList,
            ]));
        });
    }

    /**
     * 获取群成员id.
     *
     * @return array
     */
    private function getContactIdEmployeeId(int $corpId, array $member)
    {
        $contactId = 0;
        $employeeId = 0;
        if ($member['type'] == WorkContactRoomType::EMPLOYEE) {
            $employee = $this->workEmployeeService->getWorkEmployeeByWxUserIdCorpId($member['userid'], $corpId, ['id']);
            if (! empty($employee)) {
                $employeeId = $employee['id'];
            }
        } else {
            $contact = $this->workContactService->getWorkContactByCorpIdWxExternalUserId($corpId, $member['userid'], ['id']);
            if (! empty($contact)) {
                $contactId = $contact['id'];
            }
        }

        return [$contactId, $employeeId];
    }

    /**
     * 记录动态轨迹.
     */
    private function recordTrack(int $corpId, int $roomId, array $createContactRoomData)
    {
        if (empty($createContactRoomData)) {
            return;
        }

        // 客户群名称
        $room = $this->workRoomService->getWorkRoomById($roomId, ['name']);
        if (empty($room)) {
            return;
        }

        foreach ($createContactRoomData as $item) {
            // 客户0跳出循环
            $contactId = (int) $item['contact_id'];
            if ($contactId === 0) {
                continue;
            }

            $jsonContact = [
                'title' => EventName::ROOM_CREATE,
                'content' => $item['name'] . '加入了群聊[' . $room['name'] . ']',
            ];
            $data = [
                'room_id' => $item['room_id'],
                'employee_id' => 0,
                'content' => Json::encode($jsonContact),
                'corp_id' => $corpId,
                'relation_id' => $contactId,
                'event' => Event::ROOM_CREATE,
            ];
            $this->roomTrackService->createRoomTrack($data);
        }
    }
}
